// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef index_h
#define	index_h

#include "common.h"

class IndexBuilder {
public:
	IndexBuilder(uint8* data, uint dataSize, uint chunkSize);
	~IndexBuilder();
	
	bool add(uint32 value);

	uint build();

private:
	bool buildChunk();

private:
	const int chunkSize;
	
	uint32 baseIndex;

	uint8* data;
	int dataSize;
	int dataPos;

	uint32* values;
	int capacity;
	int count;
	int offset;
};

class Index {
public:
	Index(const uint8* data, uint dataSize, uint chunkSize);

private:
	#pragma pack(push, 1)
	struct Header {
		uint32 baseIndex;
		uint16 count;
		uint8 bitCount;
		uint8 reserved1;
		uint32 minValue;
		uint32 maxValue;
	};
	#pragma pack(pop)

public:

	class Iterator {
	public:
		Iterator();
		Iterator& operator=(const Iterator& that);

		uint getValueIndex() const;
		uint32 getValue() const;
		bool skip();
		bool skip(uint skipCount);

	private:
		Iterator(const Index& Index, uint32 rangeMin, uint32 rangeMax);
		bool nextChunk();
		void fetchValue();

	private:
	    uint chunkSize;
	    const Header* header;
	    uint remainingChunkCount;
	    uint32 rangeMin;
	    uint32 rangeMax;

		const uint64* payload;
		uint bitCount;
		uint32 baseValue;

		uint64 bits;
		uint availableBits;

		uint remainingCount;

		uint valueIndex;
		uint32 value;

		friend class Index;
	};

	Iterator iterator(uint32 rangeMin = 0, uint32 rangeMax = 0xFFFFFFFFU) const;
private:

	friend class IndexBuilder;

private:
	const uint8* data;
	const uint chunkSize;
	const uint chunkCount;

	friend class Index::Iterator;
};

#include "index.hpp"

#endif // index_h
